package com.xxl.job.core.rpc.netcom;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response.Status.Family;

import org.apache.wink.client.ClientResponse;
import org.apache.wink.client.Resource;
import org.apache.wink.client.RestClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.FactoryBean;

import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xxl.job.core.rpc.codec.RpcRequest;
import com.xxl.job.core.rpc.codec.RpcResponse;

/**
 * rpc proxy
 * @author xuxueli 2015-10-29 20:18:32
 */
public class NetComClientProxy implements FactoryBean<Object> {
	private static final Logger logger = LoggerFactory.getLogger(NetComClientProxy.class);

	// ---------------------- config ----------------------
	private Class<?> iface;
	String serverAddress;
	// JettyClient client = new JettyClient();
	RestClient restClient = new RestClient();
	public NetComClientProxy(Class<?> iface, String serverAddress) {
		this.iface = iface;
		this.serverAddress = serverAddress;
	}

	@Override
	public Object getObject() throws Exception {
		return Proxy.newProxyInstance(Thread.currentThread()
				.getContextClassLoader(), new Class[] { iface },
				new InvocationHandler() {
					@Override
					public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
						
						// request
						RpcRequest request = new RpcRequest();
	                    request.setServerAddress(serverAddress);
	                    request.setCreateMillisTime(System.currentTimeMillis());
	                    request.setClassName(method.getDeclaringClass().getName());
	                    request.setMethodName(method.getName());
	                    request.setParameterTypes(method.getParameterTypes());
	                    request.setParameters(args);
	                    
	                    // send
					logger.debug(serverAddress);
					Resource res = restClient.resource(serverAddress);
					ObjectMapper om = new ObjectMapper();
					String json = om.writeValueAsString(request);
					ClientResponse rst = res.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON).post(json);
					if (rst.getStatusType().getFamily() == Family.SUCCESSFUL) {
						json = rst.getEntity(String.class);
						JavaType type = om.getTypeFactory().constructParametricType(RpcResponse.class, method.getReturnType());
						RpcResponse<?> response = (RpcResponse<?>) (json == null ? null : om.readValue(json, type));
						if (response == null) {
						logger.error(">>>>>>>>>>> xxl-rpc netty response not found." + serverAddress);
						throw new Exception(">>>>>>>>>>> xxl-rpc netty response not found." + serverAddress);
						}
	                    if (response.isError()) {
	                        throw new RuntimeException(response.getError());
	                    } else {
							// if (response.getResultClass() != null && !response.getResultClass().isInstance(response.getResult())) {
							// // ObjectMapper om = new ObjectMapper();
							// json = om.writeValueAsString(response.getResult());
							// Object resp = om.readValue(json, method.getReturnType());
							// return resp;
							// }
	                        return response.getResult();
	                    }

					}
					logger.error("Error responsed from rest server, code {}, message {}", rst.getStatusCode(), rst.getMessage());
					throw new RuntimeException("Error code " + rst.getStatusCode() + " message " + rst.getMessage());
				}

			});
	}
	@Override
	public Class<?> getObjectType() {
		return iface;
	}
	@Override
	public boolean isSingleton() {
		return false;
	}

}
